/**
 * \file Vector2D.h
 * \brief Vector2D definition and declaration class
 * \author Edouard ROGE
 * \date 12 septembre 2010
 */

#ifndef _VECTOR2D_
#define _VECTOR2D_

#include <math.h>
#include "Point2D.h"

namespace Math2D
{
	namespace M2D
	{
		/**
		 * \class Vector2D
		 * \brief class with template to instantiate a 2D Vector inherit from Point2D<T>
		 */
		template < class T >
		class Vector2D : public Point2D<T>
		{
		public:
			/**
			 * \fn Vector2D();
			 * \brief Default constructor
			 */
			Vector2D();

			/**
			 * \fn Vector2D(T _x, T _y);
			 * \param _x copy _x in x
			 * \param _y copy _y in y
			 * \brief Constructor with parameter
			 */
			Vector2D(T _x, T _y);

			/**
			 * \fn Vector2D(const Point2D<T> & pStart, const Point2D<T> & pEnd);
			 * \param pStart first point
			 * \param pEnd second point
			 * \brief Constructor with parameter
			 */
			Vector2D(const Point2D<T> & pStart, const Point2D<T> & pEnd);

			/**
			 * \fn Vector2D(const Vector2D<T> & p);
			 * Copy constructor
			 */
			Vector2D(const Vector2D<T> & p);

			/**
			 * \fn ~Vector2D();
			 * \brief Destructor
			 */
			~Vector2D();

			/**
			 * \fn T Dot( const Vector2D<T> & _o ) const;
			 * \param _o 2d vector
			 * \return the dot product between (*this) and _o
			 */
			T Dot(const Vector2D<T> & _o) const;

			/**
			 * \fn T GetLength() const;
			 * \return the length of the vector
			 */
			T GetLength() const;

			/**
			 * \fn Vector2D<T> Normalize() const;
			 * \return the normalized vector
			 */
			Vector2D<T> Normalize() const;

			/**
			 * \fn Vector2D<T> & operator = (const Vector2D<T> & _o);
			 * \param _o copy _o in (*this)
			 * \return (*this) with the new value
			 * \brief operator =
			 */
			Vector2D<T> & operator = (const Vector2D<T> & _o);

			/**
			 * \fn Vector2D<T> operator - () const;
			 * \return inverse of (*this)
			 * \brief operator -
			 */
			Vector2D<T> operator -() const;

			/**
			 * \fn Vector2D<T> & operator += (const Vector2D<T> & _o);
			 * \param _o add _o values with (*this) values
			 * \return (*this) with the new value
			 * \brief operator +=
			 */
			Vector2D<T> & operator += (const Vector2D<T> & _o);

			/**
			 * \fn Vector2D<T> & operator -= (const Vector2D<T> & _o);
			 * \param _o substract _o values to (*this) values
			 * \return (*this) with the new value
			 * \brief operator -=
			 */
			Vector2D<T> & operator -= (const Vector2D<T> & _o);

			/**
			 * \fn Vector2D<T> & operator *= (T _f);
			 * \param _f scale (*this) value by _f
			 * \return (*this) with the new value
			 * \brief operator *=
			 */
			Vector2D<T> & operator *= (T _f);

			/**
			 * \fn Vector2D<T> & operator /= (T _f);
			 * \param _f scale (*this) value by _f
			 * \return (*this) with the new value
			 * \brief operator /=
			 */
			Vector2D<T> & operator /= (T _f);

			/**
			 * \fn template < class S > friend Vector2D<S> operator + (const Vector2D<S> & _o1, const Vector2D<S> & _o2);
			 * \param _o1 2d vector
			 * \param _o2 2d vector
			 * \return 2d vector
			 * \brief operator +
			 */
			template < class S > friend Vector2D<S> operator + (const Vector2D<S> & _o1, const Vector2D<S> & _o2);

			/**
			 * \fn template < class S > friend Vector2D<S> operator - (const Point2D<S> & _o1, const Point2D<S> & _o2);
			 * \param _o1 2d point (end of vector)
			 * \param _o2 2d point (start of vector)
			 * \return 2d vector
			 * \brief operator -
			 */
			template < class S > friend Vector2D<S> operator - (const Point2D<S> & _o1, const Point2D<S> & _o2);

			/**
			 * \fn template < class S > friend Vector2D<S> operator - (const Vector2D<S> & _o1, const Vector2D<S> & _o2);
			 * \param _o1 2d vector
			 * \param _o2 2d vector
			 * \return 2d vector
			 * \brief operator -
			 */
			template < class S > friend Vector2D<S> operator - (const Vector2D<S> & _o1, const Vector2D<S> & _o2);

			/**
			 * \fn template < class S > friend Vector2D<S> operator * (const Vector2D<S> & _o, S _f);
			 * \param _o 2d vector
			 * \param _f scale value
			 * \return 2d vector scaled by _f
			 * \brief operator *
			 */
			template < class S > friend Vector2D<S> operator * (const Vector2D<S> & _o, S _f);

			/**
			 * \fn template < class S > friend Vector2D<S> operator / (const Vector2D<T> & _o, S _f);
			 * \param _o 2d vector
			 * \param _f scale value
			 * \return 2d vector scaled by _f
			 * \brief operator /
			 */
			template < class S > friend Vector2D<S> operator / (const Vector2D<S> & _o, S _f);

			/**
			 * \fn template < class S > friend bool operator == (const Vector2D<S> & _o1, const Vector2D<S> & _o2);
			 * \param _o1 2d vector
			 * \param _o2 2d vector
			 * \return true if _o1 is equal to _o2
			 */
			template < class S > friend bool operator == (const Vector2D<S> & _o1, const Vector2D<S> & _o2);

			/**
			 * \fn template < class S > friend bool operator != (const Vector2D<S> & _o1, const Vector2D<S> & _o2);
			 * \param _o1 2d vector
			 * \param _o2 2d vector
			 * \return true if _o1 is not equal to _o2
			 */
			template < class S > friend bool operator != (const Vector2D<S> & _o1, const Vector2D<S> & _o2);
		};

		/** Vector2D of float */
		typedef Vector2D<float>	Vector2f;

		/** Vector2D of integer */
		typedef Vector2D<int>	Vector2i;

		template < class T >
		Vector2D< T >::Vector2D()
			: Point2D<T>()
		{
			}

		template < class T >
		Vector2D< T >::Vector2D(T _x, T _y)
			: Point2D<T>(_x, _y)
		{
			}

		template < class T >
		Vector2D< T >::Vector2D(const Point2D<T> & pStart, const Point2D<T> & pEnd)
			: Point2D<T>(pEnd.x - pStart.x, pEnd.y - pStart.y)
		{
			}

		template < class T >
		Vector2D< T >::Vector2D(const Vector2D<T> & p)
			: Point2D<T>(p.x, p.y)
		{
			}

		template < class T >
		Vector2D< T >::~Vector2D()
		{
		}

		template < class T >
		T Vector2D<T>::Dot(const Vector2D<T> & _o) const
		{
			return (this->x * _o.x + this->y * _o.y);
		}

		template < class T >
		T Vector2D<T>::GetLength() const
		{
			return (T)sqrt((double)(this->x*this->x + this->y*this->y));
		}

		template < class T >
		Vector2D<T> Vector2D<T>::Normalize() const
		{
			T length = GetLength();
			//LOG_ASSERT( (length != 0), "Length NULL");
			return (*this) / length;
		}

		template < class T >
		Vector2D<T> & Vector2D< T >::operator = (const Vector2D<T> & _o)
		{
			this->x = _o.x;
			this->y = _o.y;
			return (*this);
		}


		template < class T >
		Vector2D<T> Vector2D< T >::operator -() const
		{
			return Vector2D<T>(-this->x, -this->y);
		}

		template < class T >
		Vector2D<T>	& Vector2D< T >::operator += (const Vector2D<T> & _o)
		{
			this->x += _o.x;
			this->y += _o.y;
			return (*this);
		}

		template < class T >
		Vector2D<T>	& Vector2D< T >::operator -= (const Vector2D<T> & _o)
		{
			this->x -= _o.x;
			this->y -= _o.y;
			return (*this);
		}

		template < class T >
		Vector2D<T>	& Vector2D< T >::operator *= (T _f)
		{
			this->x *= _f;
			this->y *= _f;
			return (*this);
		}

		template < class T >
		Vector2D<T>	& Vector2D< T >::operator /= (T _f)
		{
			//LOG_ASSERT( (_f != 0) , "Divide by 0");
			this->x /= _f;
			this->y /= _f;
			return (*this);
		}
	}
}

/**
* \fn template < class S > Vector2D<S> operator + (const Vector2D<S> & _o1, const Vector2D<S> & _o2);
* \param _o1 2d vector
* \param _o2 2d vector
* \return 2d vector
* \brief operator +
*/
template < class S >
Vector2D<S>	operator + (const Vector2D<S> & _o1, const Vector2D<S> & _o2)
{
	return Vector2D<S>(_o1.x + _o2.x, _o1.y + _o2.y);
}


/**
* \fn template < class S > Vector2D<S> operator - (const Vector2D<S> & _o1, const Vector2D<S> & _o2);
* \param _o1 2d vector
* \param _o2 2d vector
* \return 2d vector
* \brief operator -
*/
template < class S >
Vector2D<S>	operator - (const Vector2D<S> & _o1, const Vector2D<S> & _o2)
{
	return Vector2D<S>(_o1.x - _o2.x, _o1.y - _o2.y);
}

/**
* \fn template < class S > Vector2D<S> operator - (const Point2D<S> & _o1, const Point2D<S> & _o2);
* \param _o1 2d point (end of vector)
* \param _o2 2d point (start of vector)
* \return 2d vector
* \brief operator -
*/
template < class S >
Vector2D<S>	operator - (const Point2D<S> & _o1, const Point2D<S> & _o2)
{
	return Vector2D<S>(_o1.x - _o2.x, _o1.y - _o2.y);
}

/**
* \fn template < class S > Vector2D<S> operator * (const Vector2D<S> & _o, S _f);
* \param _o 2d vector
* \param _f scale value
* \return 2d vector scaled by _f
* \brief operator *
*/
template < class S >
Vector2D<S>	operator * (const Vector2D<S> & _o, S _f)
{
	return Vector2D<S>(_o.x * _f, _o.y * _f);
}

/**
* \fn template < class S > Math::2D::Vector2D<S> operator / (const Math::2D::Vector2D<S> & _o, S _f);
* \param _o 2d vector
* \param _f scale value
* \return 2d vector scaled by _f
* \brief operator /
*/
template < class S >
Math::M2D::Vector2D<S>	operator / (const Math::M2D::Vector2D<S> & _o, S _f)
{
	//LOG_ASSERT( (_f != 0) , "Divide by 0");
	return Vector2D<S>(_o.x / _f, _o.y / _f);
}

/**
* \fn template < class S > bool operator == (const Math::2D::Vector2D<S> & _o1, const Math::2D::Vector2D<S> & _o2);
* \param _o1 2d vector
* \param _o2 2d vector
* \return true if _o1 is equal to _o2
*/
template < class S >
bool operator == (const Math::M2D::Vector2D<S> & _o1, const Math::M2D::Vector2D<S> & _o2)
{
	return (_o1.x == _o2.x && _o1.y == _o2.y);
}

/**
* \fn template < class S > bool operator != (const Math::M2D::Vector2D<S> & _o1, const Math::M2D::Vector2D<S> & _o2);
* \param _o1 2d vector
* \param _o2 2d vector
* \return true if _o1 is not equal to _o2
*/
template < class S >
bool operator != (const Math::M2D::Vector2D<S> & _o1, const Math::M2D::Vector2D<S> & _o2)
{
	return !(_o1 == _o2);
}

#endif // _VECTOR2D_
